package com.pan.aop;

import com.alibaba.fastjson.JSONObject;
import com.pan.controller.tools.ThreadLocalUtil;
import com.pan.domain.AopLog;
import com.pan.domain.ThreadLocalData;
import com.pan.service.IAopLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@org.aspectj.lang.annotation.Aspect
@Component
@Slf4j
public class Aspect {

    @Autowired
    private IAopLogService aopLogService;

    //切入点
    @Pointcut("@annotation(com.pan.aop.MyLog)")
    public void pt(){
        log.info("配置切入点");
    }

    //请求处理完后执行
    @AfterReturning(pointcut = "pt()")
    public void doAfterReturning(JoinPoint joinPoint){
        handleLog(joinPoint,null);
    }

    //拦截异常操作
    @AfterThrowing(value = "pt()",throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e){
        handleLog(joinPoint, e);
    }

    private void handleLog(final JoinPoint joinPoint, final  Exception e){
        //获取MyLog注解
        MyLog controllerLog = getAnnotationLog(joinPoint);
        if (controllerLog == null){
            return;
        }
        AopLog aopLog = new AopLog();
        //操作状态(0正常，1异常)
        aopLog.setStatus(0);
        //操作时间
        aopLog.setOperTime(new Date());
        if (e != null){
            aopLog.setStatus(1);
            aopLog.setErrorMsg(e.toString());
        }
        // 处理注解上的参数
        getControllerMethodDescription(joinPoint, controllerLog, aopLog);
        //获取拦截器的线程工具类对象
        ThreadLocalData userE = ThreadLocalUtil.getUserE();
        // 保存数据库
        aopLogService.saveLogMsg(aopLog.getTitle(),aopLog.getBusinessType(),aopLog.getStatus(),aopLog.getOptParam(),aopLog.getErrorMsg(),aopLog.getOperTime(),userE.getUsername(),userE.getUri(),userE.getIp());
    }

    //判断是否存在注解，存在就获取，不存在就返回null
    private MyLog getAnnotationLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null){
            return method.getAnnotation(MyLog.class);
        }

        return null;
    }

    //获取Controller层上Mylog注解中对方法的描述信息
    private void getControllerMethodDescription(JoinPoint joinPoint, MyLog myLog , AopLog aopLog){
        //设置业务类型(0其它 1增加 2修改 3删除)
        aopLog.setBusinessType(myLog.businessType().ordinal());
        //设置模块标题
        aopLog.setTitle(myLog.title());
        //对方法上的参数进行处理,处理完 userName = xxx，password = xxx
        String optParam = getAnnotationValue(joinPoint, myLog.optParam());
        aopLog.setOptParam(optParam);
    }

    //对方法上的参数进行处理
    private String getAnnotationValue(JoinPoint joinPoint,String name){
        String paramName = name;
        //获取方法中所有的参数
        Map<String,Object> params =getParams(joinPoint);
        //参数是否是动态的
        if (paramName.matches("^#\\{\\D*\\}")){
            //获取参数名，去掉#{ }
            paramName = paramName.replace("#{","").replace("}","");
            //是否是复杂的参数类型：对象，参数名
            if (paramName.contains(".")){
                String[] ss = paramName.split("\\.");
                //获取方法中对象的内容
                Object object = getValue(params,ss[0]);
                //转换为JsonObject
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(object);
                //获取值
                Object o = jsonObject.get(ss[1]);
                return String.valueOf(o);
            } else{ //简单的动态参数直接返回
                StringBuilder str = new StringBuilder();
                String[] paraNames = paramName.split(",");
                for (String paraName : paraNames){
                    String val = String.valueOf(getValue(params,paraName));
                    //组装成 userName = xxx，passwrod = xxx
                    str.append(paraName).append("=").append(val).append(",");
                }
                //去掉末尾的,
                if (str.toString().endsWith(",")){
                    String substring = str.substring(0,str.length()-1);
                    return substring;
                }else {
                    return str.toString();
                }
            }
        }
        //非动态参数直接返回
        return name;
    }

    //获取方法中的所有参数，返回Map类型
    public Map<String , Object> getParams(JoinPoint joinPoint){
        Map<String ,Object> params = new HashMap<>(8);
        //通过切入点获取方法的所有参数值
        Object[] args = joinPoint.getArgs();
        //通过切入点获取方法的所有参数名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] names = signature.getParameterNames();
        for (int i = 0; i < args.length; i++) {
            params.put(names[i] ,args[i]);
        }

        return params;
    }

    //从map中获取paramName的值，不存在返回null
    private Object getValue(Map<String ,Object> map,String paramName){
        for(Map.Entry<String, Object> entry : map.entrySet()){
            if (entry.getKey().equals(paramName)){
                return entry.getValue();
            }
        }
        return null;
    }


}
